﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;
using System.Text;
using System.Collections.Generic;
using System.Threading;

namespace eHouse
{
    public class ConnectionManager
    {
        private string ip { get; set; }
        private int port = 6969;
        private SocketManager manager { get; set; }

        public ConnectionManager(EventHandler<SocketAsyncEventArgs> serverConnected)
        {
            LoadSettings();
            this.manager = new SocketManager(ip, port);
            this.OpenConnection(serverConnected);
        }

        public ConnectionManager(string ip, EventHandler<SocketAsyncEventArgs> serverConnected)
        {
            this.ip = ip;
            this.manager = new SocketManager(ip, port);
            this.OpenConnection(serverConnected);
        }

        private void OpenConnection(EventHandler<SocketAsyncEventArgs> serverConnected)
        {
            manager.OpenConnection(serverConnected);
        }

        public void CloseConnection()
        {
            try
            {
                manager.CloseConnection();
            }
            catch (Exception) { }
        }

        public void SaveSettings()
        {
            var settings = IsolatedStorageSettings.ApplicationSettings;
            if (settings.Contains("ip"))
            {
                settings["ip"] = this.ip;
            }
            else
            {
                settings.Add("ip", this.ip);
            }

            if (settings.Contains("port"))
            {
                settings["port"] = this.port;
            }
            else
            {
                settings.Add("port", this.port);
            }
        }

        public void LoadSettings()
        {
            var settings = IsolatedStorageSettings.ApplicationSettings;
            if (settings.Contains("ip"))
            {
                this.ip = (string)settings["ip"];
            }
            else
            {
                throw new System.InvalidOperationException("There are no settings saved");
            }

            if (settings.Contains("port"))
            {
                this.port = (int)settings["port"];
            }
            else
            {
                throw new System.InvalidOperationException("There are no settings saved");
            }
        }

        public void LoadRules(string sensor, EventHandler<SocketAsyncEventArgs> onServerRespondedListener)
        {
            System.Diagnostics.Debug.WriteLine("GET RULES " + sensor.ToUpper());
            manager.Send("GET_RULES_" + sensor.ToUpper(), onServerRespondedListener);
        }

        public void RemoveRule(string sensor, string ruleId, EventHandler<SocketAsyncEventArgs> onServerRespondedListener)
        {
            System.Diagnostics.Debug.WriteLine("REMOVE RULES " + sensor.ToUpper());
            manager.Send("DELETE_RULES_" + sensor.ToUpper() + "/" + ruleId.ToUpper(), onServerRespondedListener);
        }

        public delegate void RuleAdder(Rule rule, EventHandler<SocketAsyncEventArgs> onServerRespondedListener);

        public void AddLightRule(Rule rule, EventHandler<SocketAsyncEventArgs> onServerRespondedListener)
        {
            System.Diagnostics.Debug.WriteLine("ADD LIGHT RULE");
            string desc = "";
            Dictionary<string, bool> dict = rule.Data as Dictionary<string, bool>;
            string turn = dict["VALUE"].ToString().ToUpper();
            foreach (KeyValuePair<string, bool> each in dict)
            {
                if (each.Key == "VALUE")
                {
                    continue;
                }
                desc += each.Key.ToUpper() + "/" + each.Value.ToString().ToUpper() + "/";
            }
            string data = rule.Id.ToUpper() + "\\" + rule.Time + "\\" + desc.Substring(0, desc.Length - 1) + "\\" + turn;
            rule.Formatted = data;
            manager.Send("SET_RULES_LIGHTS/" + data, onServerRespondedListener);
        }

        public void AddIrrigationRule(Rule rule, EventHandler<SocketAsyncEventArgs> onServerRespondedListener)
        {
            System.Diagnostics.Debug.WriteLine("ADD IRRIGATION RULE");
            string data = rule.Id.ToUpper() + "\\" + rule.Time + "\\" + rule.Data.ToString().ToUpper();
            rule.Formatted = data;
            manager.Send("SET_RULES_IRRIGATION/" + data, onServerRespondedListener);
        }

        public void AddTemperatureRule(Rule rule, EventHandler<SocketAsyncEventArgs> onServerRespondedListener)
        {
            System.Diagnostics.Debug.WriteLine("ADD TEMPERATURE RULE");
            rule.Formatted = rule.Id.ToUpper() + "\\" + rule.Data.ToString();
            manager.Send("SET_RULES_TEMPERATURE/" + rule.Formatted, onServerRespondedListener);
        }

        public void GetLights(EventHandler<SocketAsyncEventArgs> onServerRespondedListener)
        {
            System.Diagnostics.Debug.WriteLine("GET LIGHTS");
            manager.Send("GET_LIGHTS", onServerRespondedListener);
        }

        public void GetWindows(EventHandler<SocketAsyncEventArgs> onServerRespondedListener)
        {
            System.Diagnostics.Debug.WriteLine("GET WINDOWS");
            manager.Send("GET_WINDOWS", onServerRespondedListener);
        }

        public void GetTemperature(EventHandler<SocketAsyncEventArgs> onServerRespondedListener)
        {
            System.Diagnostics.Debug.WriteLine("GET TEMPERATURE");
            manager.Send("GET_TEMPERATURE", onServerRespondedListener);
        }

        public void GetIrrigation(EventHandler<SocketAsyncEventArgs> onServerRespondedListener)
        {
            System.Diagnostics.Debug.WriteLine("GET IRRIGATION");
            manager.Send("GET_IRRIGATION", onServerRespondedListener);
        }

        public void SwitchSensor(string sensor, string switchName, bool state, EventHandler<SocketAsyncEventArgs> onServerRespondedListener)
        {
            string sending = "SET_" + sensor.ToUpper() + "/" + switchName.ToUpper() + "/" + state.ToString().ToUpper();
            sending = sending.Replace("//", "/");
            System.Diagnostics.Debug.WriteLine(sending);
            manager.Send(sending, onServerRespondedListener);
            //onServerRespondedListener(null, new SocketAsyncEventArgs() { SocketError = SocketError.Success });
        }

        private class SocketManager
        {
            //private string ip { get; set; }
            //private int port { get; set; }
            private Socket socket;
            private SocketAsyncEventArgs connection;
            private EventHandler<SocketAsyncEventArgs> onServerConnectedListener;
            private EventHandler<SocketAsyncEventArgs> onServerRespondedListener;
            private byte[] buffer;

            private AutoResetEvent wait;

            public SocketManager(string ip, int port)
            {
                this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                this.connection = new SocketAsyncEventArgs { RemoteEndPoint = new DnsEndPoint(ip, port) };
                connection.Completed += OnConnectionToServerCompleted;
                wait = new AutoResetEvent(true);
            }

            public void Send(string text, EventHandler<SocketAsyncEventArgs> onServerRespondedListener)
            {
                //if (connection.SocketError != SocketError.Success)
                //{
                //    throw new SocketException((int)connection.SocketError);
                //}
                SocketAsyncEventArgs args = new SocketAsyncEventArgs()
                {
                    RemoteEndPoint = connection.RemoteEndPoint
                };
                buffer = Encoding.UTF8.GetBytes(text);
                args.SetBuffer(buffer, 0, buffer.Length);
                //args.Completed += onServerConnectedListener;
                args.Completed += OnMessageSent;

                socket.SendAsync(args);
                //if (!socket.SendAsync(args))
                //{
                //buffer = new byte[512];
                //args.SetBuffer(buffer, 0, buffer.Length);
                //args.Completed += OnMessageReceivedFromServer;
                wait.WaitOne();
                this.onServerRespondedListener = onServerRespondedListener;
                //socket.ReceiveAsync(args);
                //}
            }

            private void OnMessageSent(object sender, SocketAsyncEventArgs e)
            {
                if (e.LastOperation != SocketAsyncOperation.Send)
                {
                    return;
                }
                if (e.SocketError != SocketError.Success)
                {
                    OnMessageReceivedFromServer(sender, e);
                }
                SocketAsyncEventArgs args = new SocketAsyncEventArgs()
                {
                    RemoteEndPoint = connection.RemoteEndPoint
                };
                buffer = new byte[512];
                args.SetBuffer(buffer, 0, buffer.Length);
                args.Completed += OnMessageReceivedFromServer;
                socket.ReceiveAsync(args);
            }

            private void ParseText(string text, EventHandler<SocketAsyncEventArgs> listener)
            {
                string[] split = text.Split(new char[] { '/' });
                object response = null;
                SocketError result = SocketError.Success;

                if (split[0] == "QUIT")
                {
                    //wait.Set();
                    return;
                }
                else if (split[0].StartsWith("HELLO"))
                {
                    onServerConnectedListener(null, new SocketAsyncEventArgs() { SocketError = SocketError.Success });
                    //wait.Set();
                    return;
                }
                else if (split[0] == "CORRECT")
                {
                    response = null;
                }
                else if (split[0] == "TEMPERATURE")
                {
                    response = split[1];
                    Dictionary<string, string> tempSensors = new Dictionary<string, string>();
                    tempSensors.Add("VALUE", split[1]);
                    for (int i = 2; i < split.Length; i += 2)
                    {
                        tempSensors.Add(split[i], split[i + 1]);
                    }
                    response = tempSensors;
                }
                else if (split[0] == "IRRIGATION")
                {
                    response = split[1];
                }
                else if (split[0] == "LIGHTS")
                {
                    Dictionary<string, bool> lights = new Dictionary<string, bool>();
                    for (int i = 1; i < split.Length; i += 2)
                    {
                        lights.Add(split[i], split[i + 1] == "TRUE");
                    }
                    response = lights;
                }
                else if (split[0] == "WINDOWS")
                {
                    Dictionary<string, bool> windows = new Dictionary<string, bool>();
                    for (int i = 1; i < split.Length; i += 2)
                    {
                        windows.Add(split[i], split[i + 1] == "TRUE");
                    }
                    response = windows;
                }
                else if (split[0] == "RULES_LIGHT")
                {
                    ICollection<Rule> rules = new LinkedList<Rule>();
                    string[] mainSplit = text.Substring(text.IndexOf('/') + 1).Split(new char[] { '|' });
                    for (int i = 0; i < mainSplit.Length; i++)
                    {
                        if (mainSplit[i] == "")
                        {
                            continue;
                        }
                        string[] smallSplit = mainSplit[i].Split(new char[] { '\\' });
                        Rule r = new Rule()
                        {
                            Id = smallSplit[0],
                            Time = smallSplit[1],
                            Formatted = mainSplit[i]
                        };
                        string[] roomSplit = smallSplit[2].Split(new char[] { '/' });
                        Dictionary<string, bool> dict = new Dictionary<string, bool>();
                        for (int j = 0; j < roomSplit.Length; j += 2)
                        {
                            dict.Add(roomSplit[j].Substring(0, 1) + roomSplit[j].Substring(1).ToLower(), roomSplit[j + 1] == "TRUE");
                        }
                        dict.Add("VALUE", roomSplit[3] == "TRUE");
                        r.Data = dict;
                        rules.Add(r);
                    }
                    response = rules;
                }
                else if (split[0] == "RULES_IRRIGATION")
                {
                    ICollection<Rule> rules = new LinkedList<Rule>();
                    string[] mainSplit = text.Substring(text.IndexOf('/') + 1).Split(new char[] { '|' });
                    for (int i = 0; i < mainSplit.Length; i++)
                    {
                        if (mainSplit[i] == "")
                        {
                            continue;
                        }
                        string[] smallSplit = mainSplit[i].Split(new char[] { '\\' });
                        Rule r = new Rule()
                        {
                            Id = smallSplit[0],
                            Time = smallSplit[1],
                            Data = (smallSplit[2] == "TRUE"),
                            Formatted = mainSplit[i]
                        };

                        rules.Add(r);
                    }
                    response = rules;
                }
                else if (split[0] == "RULES_TEMPERATURE")
                {
                    string[] mainSplit = text.Substring(text.IndexOf('/') + 1).Split(new char[] { '\\' });
                    ICollection<Rule> rules = new LinkedList<Rule>();
                    if (mainSplit.Length > 1)
                    {
                        rules.Add(new Rule()
                        {
                            Id = mainSplit[0],
                            Time = mainSplit[1] + "ºC",
                            Data = mainSplit[1],
                            Formatted = text.Substring(text.IndexOf('/') + 1)
                        });
                    }
                    response = rules;
                }
                else
                {
                    response = text;
                    result = SocketError.InvalidArgument;
                }

                //EventHandler<SocketAsyncEventArgs> listener = onServerRespondedListener;
                //wait.Set();
                listener(response, new SocketAsyncEventArgs() { SocketError = result });
            }

            private void OnMessageReceivedFromServer(object sender, SocketAsyncEventArgs e)
            {
                EventHandler<SocketAsyncEventArgs> listener = this.onServerRespondedListener;
                wait.Set();
                if (e.SocketError != SocketError.Success)
                {
                    listener(null, e);
                }
                var message = "";
                if (e.BytesTransferred > 0)
                {
                    message = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);
                    message.Trim('\0');
                }

                if (string.IsNullOrWhiteSpace(message))
                {
                    return;
                }

                ParseText(message, listener);
            }

            public void OpenConnection(EventHandler<SocketAsyncEventArgs> serverConnected)
            {
                onServerConnectedListener = serverConnected;
                socket.ConnectAsync(connection);
            }

            private void OnConnectionToServerCompleted(object sender, SocketAsyncEventArgs e)
            {
                if (e.SocketError != SocketError.Success)
                {
                    onServerConnectedListener(sender, e);
                    return;
                }
                //connection.Completed += OnMessageReceivedFromServer;
                this.Send("HELLO_SERVER", onServerConnectedListener);
            }

            public void CloseConnection()
            {
                if (socket.Connected)
                {
                    this.Send("QUIT", null);
                    socket.Close();
                }
            }
        }
    }
}
